// This is unpublished source code. Michah Lerner 2006

package trieMatch.keywordMatcher;

import java.util.Comparator;
import java.util.Locale;

import trieMatch.Constants;
import trieMatch.Interfaces.Similarity;
import trieMatch.keywordMatcher.KeyMatch.MatchType;

/**
 * Container for keymatch item.
 * 
 * <li> <code><b>StructuredElement</b> element</code> gives the keys of the item.
 * <li> <code><b>MatchType</b> kType</code> denotes the keymatch algorithm for this item.
 * <li> <code><b>String</b> url</code> gives a resource reference for this instance of the element and kType.
 * <li> <code><b>String</b> description</code> is a textual description of the item.
 * <li> <code><b>int</b> score</code> is quantifies the weight or value of this instance. <br>
 * 
 * <br>
 * 
 * @author Michah Lerner
 * 
 */
public class KMDefinition implements Comparable<KMDefinition>, Comparator<KMDefinition> {
	final static int probFloatToIntMultiplier = 10000;
	static int RULETYPEscoreFactor = Constants.RULETYPEscoreFactor;
	static int MATCHLENGTHscoreFactor = Constants.MATCHLENGTHscoreFactor;
	static Similarity similarity = new ResultScorer_cos();// ResultScorer_prefixPhrase();

	final static Sequencer staticSequencer = new Sequencer();
	static boolean showDescriptions = true;// false;

	StructuredElement element;
	MatchType kType;
	String url;
	int score;

	/**
	 * Copy constructor.
	 * 
	 * @param valueRecord
	 *            record of values to use, including the kind of match, url, and baseline score
	 */
	KMDefinition(final KMDefinition valueRecord) {
		this.element = valueRecord.element;
		this.kType = valueRecord.kType;
		this.url = valueRecord.url;
		this.score = valueRecord.score;
	}

	/**
	 * Construct with sequencer and split string. Typical sequencer is lexical.
	 * 
	 * @param elementIndexer
	 *            implementation to sequence tokens
	 * @param s
	 *            Item to split and sequence
	 */

	KMDefinition(final String s) {
		this(staticSequencer, s);
	}

	KMDefinition(final Sequencer seq, final String s) {
		String line = s;
		boolean hasNumericScore = hasNumericScore(line);
		if (hasNumericScore) {
			final String[] prefix = line.split(",", 2);
			score = (int) (probFloatToIntMultiplier * getValue(prefix[0]));
			line = prefix.length > 1
					? prefix[1]
					: "";
		}
		final String[] fields = line.split("[ \"]*,[ \"]*", 4);
		if (fields.length != 4) {
			System.err.println("Invalid input: " + s);
		}
		element = seq.arrange(fields[0].trim());
		kType = getMatchType(fields[1]);
		url = fields[2].trim();
		setDescription(url, fields[3]);
		if (!hasNumericScore) {
			score = 1 + (1 + kType.ordinal()) * RULETYPEscoreFactor + Math.round(10f * similarity.sim(element, fields[3]));
		}
	}

	void setScore(final float score) {
		this.score = (int) (probFloatToIntMultiplier * score);
	}

	static boolean hasNumericScore(final String s) {
		return s.startsWith("#") || s.startsWith("%%");
	}

	float getValue(final String line) {
		int lp = 0;
		while (lp < line.length() && "0123456789".indexOf(line.charAt(lp)) < 0) {
			lp++;
		}
		final float score = Float.parseFloat(line.substring(lp));
		return score;
	}

	static MatchType getMatchType(final String s) {
		try {
			return MatchType.valueOf(s.toUpperCase(Locale.getDefault()).trim());
		} catch (final IllegalArgumentException e) {
			return MatchType.EXACTMATCH;
		}
	}

	static void setDescription(final String url, final String description) {
		final String oldValue = KeyMatch.symbolToName.put(url, description);
		if (oldValue != null && !oldValue.equals(description)) {
			System.err.print("ERROR: " + url + " changed from \"" + oldValue + "\" to \"" + description + "\"");
		}
	}

	String asString() {
		return String.format("%-12s", kType) + " [" + element + "] url::=" + url + " desc::=[" + KeyMatch.symbolToName.get(url)
				+ "] score::=" + score;
	}

	/**
	 * Compare two definitions according to the given string denotation of a "url", settling ties by score equals scores
	 * return the case-independent comparator result.
	 * 
	 * @param o1
	 *            first definition
	 * @param o2
	 *            second definition
	 * @return result
	 */

	public int compare(final KMDefinition o1, final KMDefinition o2) {
		if (o1.url.equalsIgnoreCase(o2.url)) { return (o1.score - o2.score); }
		return (o1.url.compareToIgnoreCase(o2.url));
	}

	public int compareTo(final KMDefinition o) {
		if (this.url.equalsIgnoreCase(o.url)) { return (this.score - o.score); }
		return this.url.compareToIgnoreCase(o.url);
	}

	public String getDescription() {
		return KeyMatch.symbolToName.get(url);
	}

	public String getScore() {
		return String.format("%d", score);
	}

	public String getMatchText() {
		return element.toString();
	}

	public String getMatchType() {
		switch (kType) {
		case KEYWORDMATCH:
			return "Keyword";
		case INORDERMATCH:
			return "InorderMatch";
		case PHRASEMATCH:
			return "PhraseMatch";
		case EXACTMATCH:
			return "ExactMatch";
		default:
			return kType.name();
		}
	}

	public String getURL() {
		return url;
	}

	@Override
	public String toString() {
		return asString();
	}

}
